frontend: i18n: Add UI support for RTL languages#5695
Conversation
9e8cc1f to
1652a37
Compare
illume
left a comment
There was a problem hiding this comment.
Thanks for these changes.
Can you please have a look at the git commits to see if they meet the contribution guidelines? We use a Linux kernel style of git commits. See the contributing guide for general context, and please see previous git commits with git log for examples.
Commits that need attention
chore: Update translations— Missingarea: descriptionprefix — e.g.frontend: HomeButton: Fix so it navigates to homeorbackend: config: Add enable-dynamic-clusters flag.
Commit guidelines
- Use atomic commits focused on a single change.
- Use the title format
<area>: <Description of changes>— description must start with a capital letter. - Keep the title under 72 characters (soft requirement).
- Explain the intention and why the change is needed.
- Make commit titles meaningful and describe what changed.
- Do not add code that a later commit rewrites; squash or reorder commits instead.
- Do not include
Fixes #NNin commit messages.
Good examples:
frontend: HomeButton: Fix so it navigates to homebackend: config: Add enable-dynamic-clusters flag
There was a problem hiding this comment.
Pull request overview
This PR adds initial RTL language support to the frontend i18n flow and introduces Arabic, Hebrew, and Urdu locale bundles.
Changes:
- Adds RTL/LTR direction metadata to supported languages and new RTL language options.
- Updates theme/document direction handling when the active language changes.
- Adds new locale JSON files for Arabic, Hebrew, and Urdu.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
frontend/src/i18n/config.ts |
Adds language metadata and RTL language entries. |
frontend/src/i18n/ThemeProviderNexti18n.tsx |
Applies document and theme direction based on i18n language. |
frontend/src/i18n/LocaleSelect/LocaleSelect.tsx |
Reads labels from the new supported language metadata. |
frontend/src/i18n/locales/ar/translation.json |
Adds Arabic translation namespace. |
frontend/src/i18n/locales/ar/glossary.json |
Adds Arabic glossary namespace. |
frontend/src/i18n/locales/ar/app.json |
Adds Arabic app namespace. |
frontend/src/i18n/locales/he/translation.json |
Adds Hebrew translation namespace. |
frontend/src/i18n/locales/he/glossary.json |
Adds Hebrew glossary namespace. |
frontend/src/i18n/locales/he/app.json |
Adds Hebrew app namespace. |
frontend/src/i18n/locales/ur/translation.json |
Adds Urdu translation namespace. |
frontend/src/i18n/locales/ur/glossary.json |
Adds Urdu glossary namespace. |
frontend/src/i18n/locales/ur/app.json |
Adds Urdu app namespace. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 12 comments.
Comments suppressed due to low confidence (12)
frontend/src/i18n/locales/ar/translation.json:293
- Arabic plural categories zero/two/few/many are left empty here, so those counts fall back to English because empty translations are treated as missing. This makes event age text partially English for common Arabic counts such as 0, 2, 3-10, and 11-99.
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_zero": "",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_one": "{{ eventDate }} ({{ count }} مرة منذ {{ firstEventDate }})",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_two": "",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_few": "",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_many": "",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_other": "{{ eventDate }} ({{ count }} مرات منذ {{ firstEventDate }})",
frontend/src/i18n/locales/ar/translation.json:392
- Arabic plural categories zero/two/few/many are empty for this label. Since empty values fall back to English, the environment-variable expander will show English text for common counts instead of Arabic.
"Show all environment variables (+{{count}} more)_zero": "",
"Show all environment variables (+{{count}} more)_one": "عرض جميع متغيرات البيئة (+{{count}} إضافية)",
"Show all environment variables (+{{count}} more)_two": "",
"Show all environment variables (+{{count}} more)_few": "",
"Show all environment variables (+{{count}} more)_many": "",
"Show all environment variables (+{{count}} more)_other": "عرض جميع متغيرات البيئة (+{{count}} إضافية)",
frontend/src/i18n/locales/ar/translation.json:425
- Arabic plural categories zero/two/few/many are empty for this label. Since empty values fall back to English, the labels expander will show English text for common counts instead of Arabic.
"Show all labels (+{{count}} more)_zero": "",
"Show all labels (+{{count}} more)_one": "عرض جميع التسميات (+{{count}} إضافية)",
"Show all labels (+{{count}} more)_two": "",
"Show all labels (+{{count}} more)_few": "",
"Show all labels (+{{count}} more)_many": "",
"Show all labels (+{{count}} more)_other": "عرض جميع التسميات (+{{count}} إضافية)",
frontend/src/i18n/locales/ar/translation.json:508
- Arabic plural categories zero/two/few/many are empty for the selected-files message. Because empty translations fall back to English, selecting 0, 2, 3-10, or 11-99 files will render English text in the Arabic UI.
"{{count}} files selected_zero": "",
"{{count}} files selected_one": "تم تحديد {{count}} ملف",
"{{count}} files selected_two": "",
"{{count}} files selected_few": "",
"{{count}} files selected_many": "",
"{{count}} files selected_other": "تم تحديد {{count}} ملفات",
frontend/src/i18n/locales/ar/translation.json:773
- Arabic plural categories zero/two/few/many are empty for the loaded-resources label. With empty translations falling back to English, common Arabic counts will render English text instead of Arabic.
"Loaded Resources ({{count}})_zero": "",
"Loaded Resources ({{count}})_one": "الموارد المحملة ({{count}})",
"Loaded Resources ({{count}})_two": "",
"Loaded Resources ({{count}})_few": "",
"Loaded Resources ({{count}})_many": "",
"Loaded Resources ({{count}})_other": "الموارد المحملة ({{count}})",
frontend/src/i18n/locales/he/translation.json:287
- Hebrew's count=2 plural entry is empty here. Because empty translations fall back to English, the event-age message for exactly two occurrences will render English in the Hebrew UI.
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_one": "{{ eventDate }} ({{ count }} times since {{ firstEventDate }})",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_two": "",
"{{ eventDate }} ({{ count }} times since {{ firstEventDate }})_other": "{{ eventDate }} ({{ count }} times since {{ firstEventDate }})",
frontend/src/i18n/locales/he/translation.json:383
- Hebrew's count=2 plural entry is empty here. Because empty translations fall back to English, the environment-variable expander will render English for exactly two hidden variables.
"Show all environment variables (+{{count}} more)_one": "Show all environment variables (+{{count}} more)",
"Show all environment variables (+{{count}} more)_two": "",
"Show all environment variables (+{{count}} more)_other": "Show all environment variables (+{{count}} more)",
frontend/src/i18n/locales/he/translation.json:413
- Hebrew's count=2 plural entry is empty here. Because empty translations fall back to English, the labels expander will render English for exactly two hidden labels.
"Show all labels (+{{count}} more)_one": "Show all labels (+{{count}} more)",
"Show all labels (+{{count}} more)_two": "",
"Show all labels (+{{count}} more)_other": "Show all labels (+{{count}} more)",
frontend/src/i18n/locales/he/translation.json:493
- Hebrew's count=2 plural entry is empty here. Because empty translations fall back to English, the selected-files message will render English when exactly two files are selected.
"{{count}} files selected_one": "{{count}} files selected",
"{{count}} files selected_two": "",
"{{count}} files selected_other": "{{count}} files selected",
frontend/src/i18n/locales/he/translation.json:755
- Hebrew's count=2 plural entry is empty here. Because empty translations fall back to English, the loaded-resources label will render English for exactly two resources.
"Loaded Resources ({{count}})_one": "Loaded Resources ({{count}})",
"Loaded Resources ({{count}})_two": "",
"Loaded Resources ({{count}})_other": "Loaded Resources ({{count}})",
frontend/src/i18n/locales/ar/translation.json:574
- These newly added Arabic diagnostic translations are empty. Since empty values are treated as missing, users selecting Arabic will see English fallback text for these diagnostic messages instead of Arabic localization.
"Init container": "",
"Ephemeral container": "",
"Restart count: {{ restartCount }}": "",
"Exit code: {{ exitCode }}": "",
"Signal: {{ signal }}": "",
frontend/src/i18n/locales/he/translation.json:559
- These newly added Hebrew diagnostic translations are empty. Since empty values are treated as missing, users selecting Hebrew will see English fallback text for these diagnostic messages instead of Hebrew localization.
"Init container": "",
"Ephemeral container": "",
"Restart count: {{ restartCount }}": "",
"Exit code: {{ exitCode }}": "",
"Signal: {{ signal }}": "",
illume
left a comment
There was a problem hiding this comment.
Thanks for this PR.
A few of the commits don't quite follow the project guidelines. We use Linux kernel style for git commits — have a look at the contributing guide and previous commits with git log.
Commits that need attention
chore: Update translations— Missingarea: descriptionprefix — e.g.frontend: HomeButton: Fix so it navigates to homeorbackend: config: Add enable-dynamic-clusters flag.
Commit guidelines
- Use atomic commits focused on a single change.
- Use the title format
<area>: <Description of changes>— description must start with a capital letter. - Keep the title under 72 characters (soft requirement).
- Explain the intention and why the change is needed.
- Make commit titles meaningful and describe what changed.
- Do not add code that a later commit rewrites; squash or reorder commits instead.
- Do not include
Fixes #NNin commit messages.
Good examples:
frontend: HomeButton: Fix so it navigates to homebackend: config: Add enable-dynamic-clusters flag
The open review comments from Copilot still need attention — can you have a look? Once addressed, please mark them as resolved.
|
btw. This is really great!! It might be that we disable the languages that you don't know in here until we found someone who can review it for us. Do you speak any of these languages well? |
Yes @illume , I am proficient in Arabic, Urdu. I can read, write, and speak Arabic and Urdu |
1d1d296 to
27a7d97
Compare
|
great! For Hebrew we could either:
|
27a7d97 to
f5da63e
Compare
illume
left a comment
There was a problem hiding this comment.
Thanks. Looks like the tests are failing now.
Can you please have a look?
yes !! solving them |
|
@illume the frontend-i18n check CI job was failing due to missing upstream translation keys. I pulled the latest main, re ran npm run i18n, and pushed the synced locale files and have fixed all the issues raised by copilot in my latest commits. I changed arEG to arSA and imported config file inside ThemeProviderNexti18n.tsx to actually use the ltr/rtl metadata |
illume
left a comment
There was a problem hiding this comment.
Thanks for this PR.
the PR has a merge-main commit; please rebase against main to keep the history clean.
Why this matters
Merge commits from main make the PR history harder to review. Please rebase your branch on top of the latest main instead, then update the PR with the rebased commits.
A few of the commits don't quite follow the project guidelines. We use Linux kernel style for git commits — have a look at the contributing guide and previous commits with git log.
Commits that need attention
chore: Update translations— Missingarea: descriptionprefix — e.g.frontend: HomeButton: Fix so it navigates to homeorbackend: config: Add enable-dynamic-clusters flag.
Commit guidelines
- Use atomic commits focused on a single change.
- Use the title format
<area>: <Description of changes>— description must start with a capital letter. - Keep the title under 72 characters (soft requirement).
- Explain the intention and why the change is needed.
- Make commit titles meaningful and describe what changed.
- Do not add code that a later commit rewrites; squash or reorder commits instead.
- Do not include
Fixes #NNin commit messages.
Good examples:
frontend: HomeButton: Fix so it navigates to homebackend: config: Add enable-dynamic-clusters flag
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
frontend/src/i18n/ThemeProviderNexti18n.tsx:121
- The direction lookup uses the raw language code from i18next, but browser detection/requested languages commonly include regions such as
ar-SA,ur-PK, orhe-ILwhilesupportedLanguagesonly has base keys. In those cases i18next can resolve translations to the base locale, but this lookup falls back toltr, so RTL users with regional browser locales will not get RTL direction.
const dir = supportedLanguages[lng]?.dir || 'ltr';
document.documentElement.lang = lng;
document.documentElement.dir = dir;
document.body.dir = dir;
frontend/src/i18n/ThemeProviderNexti18n.tsx:149
- This theme direction lookup has the same raw-language-code issue as the document direction update: regional RTL locales such as
ar-SAresolve to the Arabic resources but are not keys insupportedLanguages, so the MUI theme direction staysltr. Normalize to the resolved/base language before reading the direction metadata.
direction: supportedLanguages[lang]?.dir || 'ltr',
256cfaa to
bf83c78
Compare
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: illume, mahmoodalisha The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
thanks a lot !! @illume |



Summary
This PR introduces right-to-left (RTL) language support to Headlamp and adds new localization support for:
The UI now dynamically switches layout direction based on the selected language.
Related Issue
Fixes #ISSUE_NUMBER
Changes
Added new locale directories
frontend/src/i18n/locales/ar
frontend/src/i18n/locales/he
frontend/src/i18n/locales/ur
Updated language configuration
Modified: frontend/src/i18n/config.ts
Changes include:
Added helper:
export const isRTL = (lang: string) =>
supportedLanguages[lang]?.dir === 'rtl';
Updated locale switching logic
Modified: frontend/src/i18n/LocaleSelect/LocaleSelect.tsx
Updated language change handling:
const changeLng = (event: SelectChangeEvent) => {
const lng = event.target.value as string;
i18n.changeLanguage(lng);
};
Result
RTL languages now render correctly
Layout direction updates dynamically based on selected locale
Existing LTR languages remain unaffected
Improved internationalization support
Testing
Tested language switching with:
Verified:
Screenshots
Notes for the Reviewer
Updates i18n config to handle ltr / rtl directions, verified RTL layout switching and existing LTR behavior